package com.cskaoyan.javase.object._6clone;

/**
 * clone方法
 * 首先,这个方法使用频率不高,但它是一种新的,
 * 不同new对象的,创建对象的方式,所以它有比较独到的用途,所以有学习的必要
 * 但不作为重点.
 *
 * 1.方法的声明:
 *      protected native Object clone() throws CloneNotSupportedException;
 *      解释:
 *      a.注意,该方法的访问权限,是受保护的访问权限
 *      b.它是一个本地方法,没有方法体
 *      c.方法的返回值类型是Object
 *      d.方法无参
 *      e,throws CloneNotSupportedException 是方法抛出异常的声明列表(后面再说)
 *
 * 2.方法的作用:
 * 克隆，生物学意义上的克隆，是用生物的体细胞，经过无性繁殖，得到相同基因型后代的过程。
 *      这个方法的名字，其实已经很明显的告诉了你它的作用：
 * Java当中的克隆方法clone()有类似的作用，当你在程序中有这种需求，即希望：
 *      a.得到一个和原先对象，完全独立的新对象。
 *      b.成员仍和原先对象一致。
 * 有这种做法时，普通的做法就是再new一个一模一样的，但学习clone方法后，你就可以用该方法来做这个事情了。
 * Object类当中的clone方法默认实现:
 *      得到一个和调用方法的对象,成员完全一致,但是独立的新对象
 *
 * 3.clone方法的使用过程:
 *      a.如果不重写方法的访问权限修饰符,
 *      那么就只能够在本类中自己克隆自己(意义不大)
 *      所以为了能够在其他类中克隆这个类的对象
 *      需要重写该方法的访问权限修饰符.
 *
 *      使用clone方法的第一步就是重写该方法的访问权限修饰符,突破访问权限的限制
 *
 *      b.(选做)
 *      默认情况下Object类的clone方法,返回一个Object对象
 *      但实际上Object类本地方法的默认实现,得到的对象
 *      一定不会改变类型
 *      所以为了调用方法方便,可以选择重写返回值类型
 *
 *      c.一个类如果没有实现接口"java.lang.Cloneable"
 *      那么该类是无法进行克隆操作的
 *      强行克隆会导致程序抛出
 *      CloneNotSupportedException(克隆不支持异常)
 *      然后程序终止执行
 *
 *      所以一个类的对象如果想要进行克隆操作
 *      那么该类必须实现接口"java.lang.Cloneable"
 *
 * 以上总结,对某个类的对象做克隆操作:
 * 必须要做的是:
 *      1.重写访问权限
 *      2.该类必须实现接口"java.lang.Cloneable"
 * 选做的是:
 *      重写返回值类型,方便方法调用
 *
 * 注意事项:
 *      1.clone方法得到的对象,默认情况下,会是一个新的独立的对象,但是和原先对象的成员一致
 *      如果用"=="和未重写的equals方法和原先的对象判断,结果是false
 *      但如果重写了equals方法(用成员变量取值重写)
 *      结果是true.
 *
 *      2.Object类当中clone方法的默认实现
 *      是通过一个本地方法,来创建得到一个新对象
 *      这种创建对象的方式,完全不同于new对象
 *      不依赖于构造器
 *
 *      clone是一种全新的,和new对象平行的创建对象的方式
 *
 *      3.接口"java.lang.Cloneable"的作用是什么呢?
 *      类实现了接口"java.lang.Cloneable"为什么没有报错呢?
 *          java.lang.Cloneable是一个标记接口,被它标记(实现它)的类,允许做克隆操作
 *
 *      4.浅克隆的概念
 *          Java中Object类当中的,clone方法的默认实现,是浅克隆的:
 *              a.对象中的基本数据类型成员变量,直接拷贝一份取值,
 *                  这样拷贝的成员变量和原先的成员变量是独立的
 *              b.对象中的引用数据类型成员变量,存储的是引用,所以拷贝的也是引用
 *                  这样拷贝的引用和原先的引用仍然指向同一对象,仍然会互相影响
 *              这样浅克隆得到的两个对象,就不是完全独立的了
 *              所以需要进行深度克隆,得到一个新的,完全独立的对象
 *         深度克隆:
 *              a.深度克隆是在浅克隆基础上完成的
 *              b.然后将对象中引用所指向的对象克隆一份
 *              c.将克隆对象中的克隆引用指向步骤b中克隆对象
 *
 * @since 17:22
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        /*Student s = new Student(18, 100);
        // s能不能调用clone方法呢
        // 'clone()' has protected access in 'java.lang.Object'
        // 不能,因为没有访问权限.
        // s.clone();

        // 重写访问权限后,就可以访问了
        Student cloneStu = s.clone();

        System.out.println(s == cloneStu);
        System.out.println(s.equals(cloneStu));*/

        Student s = new Student(18, 100, new Star(10000));
        Student cloneStu = s.clone();
        s.age = 28;
        System.out.println(cloneStu.age);

        s.star.salary = 20000;
        System.out.println(cloneStu.star.salary);

    }
}

interface MyCloneable extends Cloneable {

}

/*
    这个实现接口MyCloneable是没有问题
    因为底层是用 引用 instanceof java.lang.Cloneable
    现在Student虽然不是java.lang.Cloneable的直接实现类
    但是 是间接实现类

 */
class Student implements MyCloneable {
    int age;
    double score;

    Star star;

    public Student(int age, double score) {
        System.out.println("双参构造器执行了!");
        this.age = age;
        this.score = score;
    }

    public Student(int age, double score, Star star) {
        this.age = age;
        this.score = score;
        this.star = star;
    }

    public Student() {
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
        // a.深度克隆是在浅克隆基础上完成的
        Student cloneStu = (Student) super.clone();
        // b.然后将对象中引用所指向的对象克隆一份
        // 这里需要克隆对象中star引用所指向的对象,需要克隆Star对象
        // 以下两个引用都可以完成克隆
        // star
        // cloneStu.star
        Star cloneStar = star.clone();
        // c.将克隆对象中的克隆引用指向步骤b中克隆对象
        cloneStu.star = cloneStar;
        return cloneStu;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

class Star implements Cloneable {
    double salary;

    public Star(double salary) {
        this.salary = salary;
    }

    @Override
    protected Star clone() throws CloneNotSupportedException {
        return (Star) super.clone();
    }
}